package com.hangar.eforms.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.deepoove.poi.XWPFTemplate;
import com.hangar.eforms.common.exception.BusinessException;
import com.hangar.eforms.dto.FormsDataDto;
import com.hangar.eforms.dto.FormsDataQueryDto;
import com.hangar.eforms.entity.FormsDataChangeEntity;
import com.hangar.eforms.entity.FormsDataEntity;
import com.hangar.eforms.mapper.FormsDataMapper;
import com.hangar.eforms.vo.FormsDataChangeVo;
import com.hangar.eforms.vo.FormsDataVo;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class FormsDataService {
    public static final String CHANGER = "张三";

    @Resource
    private FormsDataMapper formsDataMapper;
    @Resource
    private FormsDataChangeService changeService;

    @Value("${report.root-path}")
    private String reportRootPath;
    @Value("${report.libreoffice-command}")
    private String libreofficeCommand;

    public int deleteById(Integer id) {
        return formsDataMapper.deleteById(id);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public FormsDataDto insert(FormsDataDto dto) {
        FormsDataEntity record = FormsDataEntity.builder().templateId(dto.getTemplateId())
                .useUnit(dto.getUseUnit()).equipmentCategory(dto.getEquipmentCategory())
                .equipmentVariety(dto.getEquipmentVariety()).equipmentModel(dto.getEquipmentModel())
                .equipmentCode(dto.getEquipmentCode()).certificateNo(dto.getCertificateNo())
                .examineYear(dto.getExamineYear()).examineMonth(dto.getExamineMonth())
                .examineDay(dto.getExamineDay()).formData(JSONUtil.toJsonStr(dto.getFormData())).build();
        formsDataMapper.insert(record);
        dto.setId(record.getId());
        return dto;
    }

    public IPage<FormsDataVo> list(Page<FormsDataVo> page, FormsDataQueryDto queryDto) {
        return formsDataMapper.list(page, queryDto);
    }

    public FormsDataVo loadById(Integer id) {
        FormsDataEntity entity = formsDataMapper.loadById(id);
        FormsDataVo formsDataVo = new FormsDataVo();
        BeanUtil.copyProperties(entity, formsDataVo, "formData");
        formsDataVo.setFormData(JSONUtil.parseObj(entity.getFormData()).toBean(Map.class));
        return formsDataVo;
    }

    public FormsDataVo view(Integer id) {
        FormsDataEntity entity = formsDataMapper.loadById(id);
        FormsDataVo formsDataVo = new FormsDataVo();
        BeanUtil.copyProperties(entity, formsDataVo, "formData");
        formsDataVo.setFormData(JSONUtil.parseObj(entity.getFormData()).toBean(Map.class));
        List<FormsDataChangeVo> changeList = changeService.allByFormDataId(id);
        formsDataVo.setFormDataChangeList(changeList.stream().collect(Collectors.groupingBy(FormsDataChangeVo::getFieldName)));
        return formsDataVo;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void update(FormsDataDto dto) {
        FormsDataEntity dBFormsData = formsDataMapper.loadById(dto.getId());
        //过滤掉旧数据中数指标项值为空的
        Map<String, Object> originData = MapUtil.filter(JSONUtil.parseObj(dBFormsData.getFormData()).toBean(Map.class),
                m -> ObjectUtil.isNotNull(m.getValue()) && StrUtil.isNotBlank(m.getValue().toString()));
        List<FormsDataChangeEntity> changeList = new ArrayList<>();
        for (String key : originData.keySet()) {
            if (!originData.get(key).equals(dto.getFormData().get(key))) {
                FormsDataChangeEntity changeEntity = FormsDataChangeEntity.builder()
                        .formDataId(dto.getId())
                        .fieldName(key)
                        .beforeValue(originData.get(key).toString())
                        .afterValue(ObjectUtil.isNull(dto.getFormData().get(key)) ? null : dto.getFormData().get(key).toString())
                        .changer(CHANGER).changeTime(DateUtil.date()).build();
                changeList.add(changeEntity);
            }
        }

        BeanUtil.copyProperties(dto, dBFormsData, "formData");
        dBFormsData.setFormData(JSONUtil.toJsonStr(dto.getFormData()));
        formsDataMapper.update(dBFormsData);
        if (changeList.size() > 0) {
            changeService.batchInsert(changeList);
        }
    }

    /**
     * 使用检验报告模板导出pdf
     *
     * @param id       电子表单数据ID
     * @param response response
     */
    public void exportReport(Integer id, HttpServletResponse response) {
        long startTime = System.currentTimeMillis();
        FormsDataVo vo = getExportDataMap(id);
        System.out.println(String.format("获取填充数据耗时：%s ms", System.currentTimeMillis() - startTime));
        try {
            String pdfName = reportRootPath + String.format("template%s_data%s.pdf", vo.getTemplateId(), vo.getId());
            File pdfFile = new File(pdfName);
            startTime = System.currentTimeMillis();
            fillWordAndConvertPdf(vo);
            System.out.println(String.format("数据填充及转换总耗时：%s ms", System.currentTimeMillis() - startTime));
            startTime = System.currentTimeMillis();
            FileInputStream inputStream = new FileInputStream(pdfFile);
            ServletOutputStream outputStream = response.getOutputStream();
            String filename = URLEncoder.encode("起重机械定期检验报告.pdf", StandardCharsets.UTF_8);
            response.setHeader("Content-Disposition", "attachment; filename=%s".formatted(new Object[]{filename}));
            response.setContentType("application/pdf");
            IoUtil.copy(inputStream, outputStream);
            outputStream.close();
            inputStream.close();
            System.out.println(String.format("输出PDF文件耗时：%s ms", System.currentTimeMillis() - startTime));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("生成PDF报告失败", e);
            throw new BusinessException("生成PDF报告失败");
        }
    }

    /**
     * 填充word模板后再由word转pdf
     *
     * @param vo 表单数据
     * @throws IOException
     */
    private void fillWordAndConvertPdf(FormsDataVo vo) throws IOException, InterruptedException {
        long startTime = System.currentTimeMillis();
        InputStream templateInputStream = ResourceUtil.getStream("templates/起重机械定期检验记录模板.docx");
        XWPFTemplate template = XWPFTemplate.compile(templateInputStream).render(vo.getFormData());
        String docxName = String.format("template%s_data%s.docx", vo.getTemplateId(), vo.getId());
        File docxFile = new File(reportRootPath);
        if (FileUtil.exist(docxFile)) {
            FileUtil.mkdir(docxFile);
        }
        if (!docxFile.exists()) {
            docxFile.mkdirs();
        }
        FileOutputStream out;
        out = new FileOutputStream(reportRootPath + docxName);
        template.writeAndClose(out);
        out.flush();
        out.close();
        System.out.println(String.format("数据填充耗时：%s ms", System.currentTimeMillis() - startTime));

        startTime = System.currentTimeMillis();
        //调用libreoffice命令将word转pdf
        Process process = Runtime.getRuntime().exec(String.format(libreofficeCommand, docxName));
        //等待转换进程完成再往下执行
        process.waitFor();
        System.out.println(String.format("word转pdf耗时：%s ms", System.currentTimeMillis() - startTime));
    }

    /**
     * 获取导出到pdf的数据map
     *
     * @param id 表单数据ID
     * @return 导出数据map
     */
    private FormsDataVo getExportDataMap(Integer id) {
        FormsDataVo vo = loadById(id);
        List<String> symbolFields = Arrays.asList("hygrometerUseFlag", "steelTapeRulerUseFlag", "steelRulerUseFlag",
                "vernierCalipersUseFlag", "feelerGaugeUseFlag", "dialGaugeUseFlag", "laserRangefinderUseFlag",
                "multimeterUseFlag", "insulationResistanceUseFlag", "groundResistanceUseFlag");
        symbolFields.stream().filter(vo.getFormData()::containsKey)
                .forEach(key -> {
                    if (ObjectUtil.isNotNull(vo.getFormData().get(key)) &&
                            (vo.getFormData().get(key).toString().equals("1") || vo.getFormData().get(key).toString().equals("true"))) {
                        vo.getFormData().put(key, "√");
                    } else {
                        vo.getFormData().put(key, "");
                    }
                });
        return vo;
    }
}
